#include "NetworkManager.h"

#include <iostream>
using namespace std;

int NetworkManager::Init( unsigned short port )
{
	WSADATA wsd;
	WSAStartup( WINSOCK_VERSION, &wsd );

	m_scCom = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );

	if( m_scCom == INVALID_SOCKET )
	{
		return -1;
	}

	unsigned long value = 1;
	if( ioctlsocket( m_scCom, FIONBIO, &value ) == SOCKET_ERROR )
		return -1;

	SOCKADDR_IN binder;
	binder.sin_family = AF_INET;
	binder.sin_port = htons( port );
	binder.sin_addr.s_addr = INADDR_ANY;

	if( bind( m_scCom, (sockaddr*)&binder, sizeof( binder ) ) == SOCKET_ERROR )
		return -1;

	memset( m_cBuffer, 0, 1024 );
	m_bFresh = false;
	m_nClientCount = 0;

	return 0;
};

int NetworkManager::Shutdown()
{
	WSACleanup();
	return 0;
}

bool NetworkManager::peekBuffer()
{
	if( m_bFresh )
		return m_bFresh;
	else
	{
		int stuff = sizeof( m_saAddr );
		int error = recvfrom( m_scCom, m_cBuffer, 1024, 0, (sockaddr*)&m_saAddr, &stuff );
		if( error > 0 )
		{
			if( client_cnct == m_cBuffer[4] )
			{
				AddActiveClientToList();
				SendToActiveClient( server_cntrcv, nullptr, 0 );

				cout << "[Network] Client [" << GetActiveClient() << "] connected.\n";

				return peekBuffer();
			}
			else
			{
				int aClient = GetActiveClient();
				if( aClient > -1 )
					m_ConnectedClients[ aClient ].time = 0.0f;
				
				m_bFresh = true;
				return m_bFresh;
			}
		}
		else
		{
			return false;
		}
	}
}

int NetworkManager::readBuffer( char* _outBuffer, unsigned int _length )
{
	m_bFresh = false;
	memcpy( _outBuffer, m_cBuffer, _length );
	return GetActiveClient();
}

void NetworkManager::Update( float fDT )
{
	for( int i = 0; i < MAX_CLIENT_COUNT; ++i )
	{
		if( m_ConnectedClients[i].active )
		{
			m_ConnectedClients[i].time += fDT;

			if( m_ConnectedClients[i].time > 1.0f )
			{
				cout << "[Network] Client [" << i << "] disconnected.\n";
				RemoveClientFromList( i );
			}
		}
	}
}

int NetworkManager::GetActiveClient()
{
	for( int i = 0; i < MAX_CLIENT_COUNT; ++i )
	{
		if( m_saAddr.sin_addr.s_addr == m_ConnectedClients[i].address.sin_addr.s_addr )
		{
			if( m_saAddr.sin_port == m_ConnectedClients[i].address.sin_port )
			{
				return i;
			}
		}
	}

	return -1;
}

int NetworkManager::AddActiveClientToList()
{
	for( int i = 0; i < MAX_CLIENT_COUNT; ++i )
	{
		if( m_saAddr.sin_addr.s_addr == m_ConnectedClients[i].address.sin_addr.s_addr )
		{
			if( m_saAddr.sin_port == m_ConnectedClients[i].address.sin_port )
			{
				return i;
			}
		}
	}

	for( int i = 0; i < MAX_CLIENT_COUNT; ++i )
	{
		if( !m_ConnectedClients[i].active )
		{
			m_ConnectedClients[i].active = true;
			m_ConnectedClients[i].lastSequence = 1;
			m_ConnectedClients[i].time = 0.0f;

			//m_ConnectedClients[i].address.sin_addr.s_addr = m_saAddr.sin_addr.s_addr;
			m_ConnectedClients[i].address.sin_port = m_saAddr.sin_port;
			m_ConnectedClients[i].address.sin_addr.S_un = m_saAddr.sin_addr.S_un;

			m_nClientCount++;
			return i;
		}
	}

	return -1;
}

int NetworkManager::RemoveClientFromList( unsigned int _ID )
{
	if( _ID < MAX_CLIENT_COUNT )
	{
		m_ConnectedClients[ _ID ].active = false;
		m_ConnectedClients[ _ID ].lastSequence = 0;
		m_ConnectedClients[ _ID ].time = 0.0f;

		m_nClientCount--;
		return 0;
	}

	return -1;
}

int NetworkManager::SendToActiveClient( char _type, char* buffer, int _length )
{
	int m_nActiveID = GetActiveClient();

	if( m_nActiveID < 0 )
		return -1;

	// create the new message buffer
	char* sendBuffer = new char[ 5 + _length ];

	// set the sequence
	int sequence = m_ConnectedClients[ m_nActiveID ].lastSequence + 1;
	memcpy( sendBuffer, &sequence, 4 );

	// set the type
	sendBuffer[ 4 ] = _type;

	// copy over the message information
	memcpy( sendBuffer + 5, buffer, _length ); 

	sendto( m_scCom, sendBuffer, 5 + _length, 0, (sockaddr*)&m_saAddr, sizeof( m_saAddr ) );

	delete sendBuffer;
	return 0;
}

void NetworkManager::SendToAll( char _type, char* buffer, int _length )
{
	for( int i = 0; i < MAX_CLIENT_COUNT; ++i )
	{
		if( m_ConnectedClients[i].active )
		{
			SetActiveClient( i );
			SendToActiveClient( _type, buffer, _length );
		}
	}
}

int NetworkManager::SetActiveClient( unsigned int ID )
{
	if( ID < MAX_CLIENT_COUNT )
	{
		m_saAddr.sin_addr.s_addr = m_ConnectedClients[ID].address.sin_addr.s_addr;
		m_saAddr.sin_port = m_ConnectedClients[ID].address.sin_port;
	}
	return 0;
}